/*************************************************************************
	> File Name: my_popen.c
	> Author: 
	> Mail: 
	> Created Time: 2021年03月20日 星期六 13时50分52秒
 ************************************************************************/

#include "head.h"

static pid_t *childPid = NULL;
static int maxsize = 0;

FILE *my_popen(const char *cmd, const char *type) {
	int pfd[2];
	FILE *fp;
	pid_t pid;
	// 判断传进来的type
	if ((type[0] != 'r' && type[0] != 'w') || type[1] != '\0') {
		errno = EINVAL;
		return NULL;
	}
    // 初始化数组
	if (childPid == NULL) {
		maxsize = sysconf(_SC_OPEN_MAX);
		if ((childPid = (pid_t *)calloc(maxsize, sizeof(pid_t))) == NULL) {
			return NULL;
		}
	}

	// 创建管道
	if (pipe(pfd) < 0) {
		return NULL;
	}
	// fork()
	if ((pid = fork()) < 0) {
		return NULL;
	}

    // 子进程，跟父进程的操作是反的
	if (pid == 0) {
		if (type[0] == 'r') {
			// 如果是read，则关闭读端
			close(pfd[0]);
			// 确认写端是标准输出（往标准输出中写内容）
			if (pfd[1] != STDOUT_FILENO) {
				dup2(pfd[1], STDOUT_FILENO);
				close(pfd[1]);
			}
		} else {
			// 如果是write，则关闭写端
			close(pfd[1]);
			// 确认读端是标准输入（从标准输入中读内容）
			if (pfd[0] != STDIN_FILENO) {
				dup2(pfd[0], STDIN_FILENO);
				close(pfd[0]);
			}
		}
		// 调用shell执行cmd
		execl("/bin/sh", "sh", "-c", cmd, NULL);
		exit(0);
	}
	// 父进程
	if (type[0] == 'r') {
		// 父进程读，关闭写端
		close(pfd[1]);
		// 通过文件描述符获取流
		if ((fp = fdopen(pfd[0], type)) == NULL) {
			return NULL;
		}
	} else {
		// 父进程写，关闭读端
		close(pfd[0]);
		// 通过文件描述符获取流
		if ((fp = fdopen(pfd[1], type)) == NULL) {
			return NULL;
		}
	}
	// 将孩子的pid存进数组中
	childPid[fileno(fp)] = pid;
	return fp;
}


int my_pclose(FILE *fp) {
	int status, pid, fd;
	// 通过流获取文件描述符
	fd = fileno(fp);
	// 从数组中获取孩子的pid
	pid = childPid[fd];
	if (pid == 0) {
		errno = EINVAL;
		return -1;
	}
	fflush(fp);
	// close
	close(fd);
	// wait4()
	waitpid(pid, &status, 0);
	return status;
}